// STM32F100 and SI4032 RTTY transmitter
// released under GPL v.2 by anonymous developer
// enjoy and have a nice day
// ver 1.5a

#include <stm32f10x_gpio.h>
#include <stm32f10x_tim.h>
#include <stm32f10x_spi.h>
#include <stm32f10x_tim.h>
#include <stm32f10x_usart.h>
#include <stm32f10x_adc.h>
#include <stm32f10x_rcc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <misc.h>

#include "config.h"
#include "delay.h"
#include "f_rtty.h"
#include "init.h"
#include "radio.h"
#include "ublox.h"
#include "util.h"
#include "satpredict.h"
#include "morse.h"


#define GREEN  GPIO_Pin_7	// Inverted
#define RED    GPIO_Pin_8	// Think this is inverted too

enum { STARTUP, RTTY, MORSE };
volatile int current_mode = STARTUP;

// Telemetry Data to Transmit
unsigned int send_count;
int voltage;
int8_t si4032_temperature;
GPSEntry gpsData;

float tx_freq = TRANSMIT_FREQUENCY;
char callsign[15] = {CALLSIGN};
uint16_t CRC_rtty = 0x12ab;
char buf_rtty[200];

// Volatile Variables, used within interrupts.
volatile int adc_bottom = 2000;
volatile char flaga = 0;	// GPS Status Flags
volatile int led_enabled = 1;	// Flag to disable LEDs at altitude.

volatile unsigned char pun = 0;
volatile unsigned int cun = 10;
volatile unsigned char tx_on = 0;
volatile unsigned int tx_on_delay;
volatile unsigned char tx_enable = 0;
rttyStates send_rtty_status = rttyZero;
volatile char *tx_buffer;
volatile uint16_t button_pressed = 0;
volatile uint8_t disable_armed = 0;


void collect_telemetry_data (void);
void send_rtty_packet (void);
uint16_t gps_CRC16_checksum (char *string);
void morse_beacon (void);


//
// GPS data processing

void USART1_IRQHandler (void)
{
  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    ublox_handle_incoming_byte((uint8_t) USART_ReceiveData(USART1));
  else
    //if (USART_GetITStatus(USART1, USART_IT_ORE) != RESET)
    //  USART_ReceiveData(USART1);
    //else			/* wut */
    USART_ReceiveData(USART1);
}


//
// Symbol Timing Interrupt
// In here symbol transmission occurs.

void TIM2_IRQHandler(void)
{
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
      TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

      //

      if (ALLOW_DISABLE_BY_BUTTON)
	{
	  if (ADCVal[1] > adc_bottom)
	    {
	      button_pressed++;
	      if (button_pressed > (BAUD_RATE / 3))
		{
		  disable_armed = 1;
		  GPIO_SetBits(GPIOB, RED);
		}
	    }
	  else
	    {
	      if (disable_armed) GPIO_SetBits(GPIOA, GPIO_Pin_12);
	      button_pressed = 0;
	    }

	  if (button_pressed == 0)
	    adc_bottom = ADCVal[1] * 1.1; // dynamical reference for power down level
	}

      //

      if (tx_on)
	{
	  if (current_mode == RTTY)
	    {
	      send_rtty_status = send_rtty((char *) tx_buffer);

	      if (!disable_armed)
		{
		  if (send_rtty_status == rttyEnd)
		    {
		      if (led_enabled) GPIO_ResetBits(GPIOB, RED);
		      if (*(++tx_buffer) == 0)
			{
			  tx_on = 0;
			  // Reset the TX Delay counter, which is decremented at the symbol rate.
			  tx_on_delay = TX_DELAY / (1000/BAUD_RATE);
			  tx_enable = 0;
			  radio_disable_tx();
			}
		    }

		  if (send_rtty_status == rttyOne)
		    {
		      radio_rw_register(0x73, RTTY_DEVIATION, 1);
		      if (led_enabled) GPIO_SetBits(GPIOB, RED);
		    }

		  if (send_rtty_status == rttyZero)
		    {
		      radio_rw_register(0x73, 0x00, 1);
		      if (led_enabled) GPIO_ResetBits(GPIOB, RED);
		    }
		}
	    }
	} /* C should not look like LISP... */

      // Delay between Transmissions Logic.
      // tx_on_delay is set at the end of a RTTY transmission above, and counts down
      // at the interrupt rate. When it hits zero, we set tx_enable to 1, which allows
      // the main loop to continue.

      if (!tx_on && --tx_on_delay == 0)
	{
	  tx_enable = 1;
	  tx_on_delay--;
	}

      // Green LED Blinking Logic
      if (--cun == 0)
	{
	  if (pun)
	    {
	      // Clear Green LED.
	      if (led_enabled) GPIO_SetBits(GPIOB, GREEN);
	      pun = 0;
	    }
	  else
	    {
	      // If we have GPS lock, set LED
	      if (flaga & 0x80)
		if (led_enabled) GPIO_ResetBits(GPIOB, GREEN);
	      pun = 1;
	    }
	  // Wait 200 symbols.
	  cun = 200;
	}
    }
}


int main (void)
{
  RCC_Conf();
  NVIC_Conf();
  init_port();

  init_timer (BAUD_RATE);

  delay_init();
  ublox_init();

  GPIO_SetBits (GPIOB, RED);
  GPIO_SetBits (GPIOB, GREEN); // NOTE - Green LED is inverted. (Reset to activate, Set to deactivate)
  USART_SendData (USART3, 0xc);	/* ? */

  radio_soft_reset();
  radio_set_tx_frequency (tx_freq); // setting RTTY TX frequency
  radio_rw_register (0x6D, 00 | (TX_POWER & 0x0007), 1);	// setting TX power
  radio_rw_register (0x71, 0x00, 1); // initial RTTY modulation
  radio_rw_register (0x13, 0x00, 1); // Was 0xF0(?) Temperature Value Offset
  radio_rw_register (0x12, 0x20, 1); // Temperature Sensor Calibration
  radio_rw_register (0x0f, 0x80, 1); // ADC configuration

  tx_buffer = buf_rtty;
  tx_on = 0;
  tx_enable = 1;

  // Why do we have to do this again?
  spi_init();
  radio_set_tx_frequency(tx_freq);
  radio_rw_register(0x71, 0x00, 1);
  init_timer(BAUD_RATE);

  radio_enable_tx();

  //

  int pass = 0;

  while (1)
    {
      if (tx_on == 0 && tx_enable)
	switch (current_mode)
	  {
	  case STARTUP:
	    collect_telemetry_data();
	    send_rtty_packet();
	    current_mode = RTTY;
	    break;

	  case RTTY:
	    radio_disable_tx();
	    sat_update();

	    tx_freq = TRANSMIT_FREQUENCY;
#if 0
	    for (int ind = 0; ind < numsats; ind++)
	      if ((satstatus[ind].elevation > HORIZON)
		  || (satstatus[ind].elevation > 0) && ((gpsData.alt_raw/1000) >= FLYING))
		tx_freq = satdata[ind].uplink;

	    radio_set_tx_frequency(tx_freq); /* hopefully this is doable.. */
	    radio_rw_register(0x71, 0x00, 1);
	    current_mode = MORSE;
#endif
	    current_mode = STARTUP;
	    break;

	  case MORSE:
	    morse_beacon ();
	    current_mode = STARTUP;
	  }

      else
	{
	  NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, DISABLE);
	  __WFI();
	}

      pass++;
    }
}


void collect_telemetry_data (void)
{
  send_count++;
  si4032_temperature = radio_read_temperature();
  voltage = ADCVal[0] * 600 / 4096;
  ublox_get_last_data(&gpsData);

  if (gpsData.fix >= 3)
    {
      flaga |= 0x80;
      if ((gpsData.alt_raw/1000) > 1000) /* Disable LEDs if altitude is > 1000m */
	led_enabled = 0;   /* not living in Armidale again anyway.. */
      else
	led_enabled = 1;
    }
  else
    {
      flaga &= ~0x80;  // No GPS fix.
      led_enabled = 1; // Enable LEDs when there is no GPS fix (i.e. during startup)
      gpsData.lat_raw = 0; // Null out lat / lon data to avoid spamming invalid positions
      gpsData.lon_raw = 0;
    }
}


void send_rtty_packet (void)
{
  uint8_t  lat_d  = (uint8_t)  abs(gpsData.lat_raw / 10000000);
  uint32_t lat_fl = (uint32_t) abs(abs(gpsData.lat_raw) - lat_d * 10000000) / 1000;
  uint8_t  lon_d  = (uint8_t)  abs(gpsData.lon_raw / 10000000);
  uint32_t lon_fl = (uint32_t) abs(abs(gpsData.lon_raw) - lon_d * 10000000) / 1000;
  uint8_t  speed_kph = (uint8_t)((float)gpsData.speed_raw*0.0036);

  sprintf(buf_rtty, "$$$$%s,%d,%02u:%02u:%02u,%s%d.%05ld,%s%d.%05ld,%ld,%d,%d,%d,%d",
	  callsign, send_count, gpsData.hours, gpsData.minutes, gpsData.seconds,
	  gpsData.lat_raw < 0 ? "-" : "", lat_d, lat_fl, gpsData.lon_raw < 0 ? "-" : "", lon_d, lon_fl,
	  (gpsData.alt_raw / 1000), speed_kph, gpsData.sats_raw, voltage*10, si4032_temperature );

  CRC_rtty = string_CRC16_checksum(buf_rtty + 9); /* sensitive to the $$$$ stuff */
  sprintf(buf_rtty, "%s*%04X\n\n", buf_rtty, CRC_rtty & 0xffff);

  // if on the non-satellite frequency indicate what's being tx'd too
  // I hate the temporary RAM usage here.. :|

  if (tx_freq == TRANSMIT_FREQUENCY)
    for (int i = 0; i < numsats; i++)
      if ((satstatus[i].elevation > HORIZON)
	  || ((satstatus[i].elevation > 0) && ((gpsData.alt_raw/1000) >= FLYING)))
  	sprintf (buf_rtty, "%sSAT %s d=%f\n", buf_rtty, satdata[i].name, satstatus[i].dopplerhz);

  //

  tx_buffer = buf_rtty;
  start_bits = RTTY_PRE_START_BITS;
  radio_enable_tx();
  tx_on = 1;
  return;
}


void morse_beacon (void)
{
  /* just testing satellite repeaters atm */
  send_morse ("EEEEE VK2CJB VK2CJB +");
}
